#pragma once

#include "Data.hpp"
#include "httplib.h"
#include <Windows.h>

namespace cloud
{
#define SERVER_IP "47.108.185.92"
#define SERVER_PORT 8080

    class Backup
    {
    public:
        Backup(const std::string &backup_dir, const std::string &backup_file)
            : backup_dir_(backup_dir)
        {
            data_ = new DataManager(backup_file);
        }

        // 获取文件最新的唯一标识，Etag
        std::string GetFileIdentifier(const std::string &filename)
        {
            FileUtil fu(filename);
            std::stringstream ss;
            ss << fu.FileName() << "-" << fu.FileSize() << "-" << fu.LastModifyTime();
            return ss.str();
        }

        // 上传文件接口
        bool Upload(const std::string &filename)
        {
            // 1. 获取文件内容
            FileUtil fu(filename);
            std::string body;
            fu.GetContent(&body);

            // 2. 搭建 http 客户端上传数据
            httplib::Client client(SERVER_IP, SERVER_PORT);
            httplib::MultipartFormData item;
            item.content = body;
            item.filename = fu.FileName();
            item.name = "file";
            item.content_type = "application/octet-stream";
            httplib::MultipartFormDataItems items;
            items.push_back(item);
            auto ret = client.Post("/upload", items);
            if (!ret || ret->status != 200)
            {
                std::cout << "the file upload failed !" << std::endl;
                return false;
            }

            return true;
        }

        // 判断一个文件是否需要上传, 需要上传返回true, 不需要就返回 false
        bool IsNeedUpload(const std::string &filename)
        {
            // 判断一个文件是否需要上传备份 1. 文件是否是新增的 2. 文件不是新增的，但是被修改过
            // 文件是否是新增的：看一看有没有历史备份信息
            // 文件不是新增的，但是被修改过：有历史备份信息，但是历史的唯一标识Etag与当前最新的Etag不一致
            std::string etag;
            if (data_->GetEtagByPath(filename, &etag))
            {
                // 有备份信息, 获取文件当前最新的 etag
                std::string new_etag = GetFileIdentifier(filename);
                if (new_etag == etag)
                {
                    // 该文件上传后没有被修改过，所以不需要重新上传
                    return false;
                }
            }

            // 如果一个文件比较大，它正在慢慢地拷贝到备份目录下，是需要比较长的时间的
            // 那么这样每次遍历备份目录时，都会发现该文件的 etag 在发生变化，那么每次
            // 都要对该文件进行上传。一个几十G的文件就会上传上百次。
            // 因此当我们判断一个文件有一段时间没有被修改过了，我们才进行上传
            FileUtil fu(filename);
            // 我们规定一个文件如果在3秒钟之内被修改过，那么就任务该文件还在修改中，就不需要进行上传
            if (time(nullptr) - fu.LastModifyTime() < 3)
            {
                return false;
            }

            return true;
        }

        bool RunModule()
        {
            while (1)
            {
                // 1. 遍历备份目录下的所有文件
                FileUtil fu(backup_dir_);
                std::vector<std::string> arry;
                fu.ScanDirectory(&arry);

                // 2. 逐个判断文件是否需要上传
                for (auto &file : arry)
                {
                    if (!IsNeedUpload(file))
                    {
                        continue;
                    }

                    // 3. 上传需要上传的文件
                    if (Upload(file))
                    {
                        data_->Insert(file, GetFileIdentifier(file));
                    }
                }
                Sleep(1);
            }
            return true;
        }

    private:
        std::string backup_dir_; // 备份文件所在目录
        DataManager *data_;      // 数据管理类
    };
}